package org.zhuzx.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.swing.filechooser.FileSystemView;

/**
 * 文件操作工具类
 * @author	zhuzx	2017年3月11日
 */
public class FileOperationUtil {
	
	private static final String HEX = "0123456789ABCDEF";

	public static String getMd5(byte[] bytes) {
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			digest.update(bytes);
			byte[] md5Bytes = digest.digest();
			return bytesToHex(md5Bytes);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static String bytesToHex(byte[] bytes) {
		StringBuilder sb = new StringBuilder(bytes.length * 2);
		for (byte b : bytes) {
			sb.append(HEX.charAt((b >> 4) & 0X0F));
			sb.append(HEX.charAt(b & 0X0F));
		}
		return sb.toString();
	}
	
	public static String gzipCompressToString(String original) {
		byte[] bytes = gzipCompressToBytes(original);
		return new String(bytes, StandardCharsets.ISO_8859_1);
	}
	
	public static byte[] gzipCompressToBytes(String original) {
		byte[] bytes = original.getBytes(StandardCharsets.UTF_8);
		return gzipCompressToBytes(bytes);
	}
	
	public static byte[] gzipCompressToBytes(byte[] bytes) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			GZIPOutputStream gzipOut = new GZIPOutputStream(out, UnifiedConstants.IO_BUFFER_SIZE);
			gzipOut.write(bytes);
			gzipOut.finish();
			gzipOut.flush();
			gzipOut.close();
			return out.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static String gzipUnCompress(String original) {
		byte[] bytes = original.getBytes(StandardCharsets.ISO_8859_1);
		return gzipUnCompress(bytes);
	}
	
	public static String gzipUnCompress(byte[] bytes) {
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		return gzipUnCompress(in);
	}
	
	public static byte[] gzipUnCompressToBytes(byte[] bytes) {
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		return gzipUnCompressToBytes(in);
	}
	
	public static String gzipUnCompress(InputStream in) {
		byte[] bytes = gzipUnCompressToBytes(in);
		return new String(bytes, StandardCharsets.UTF_8);
	}
	
	public static byte[] gzipUnCompressToBytes(InputStream in) {
		try {
			GZIPInputStream gzipIn = new GZIPInputStream(in, UnifiedConstants.IO_BUFFER_SIZE);
			return GeneralUtil.getBytes(gzipIn);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static byte[] zipCompressToBytes(String[] filenames, byte[][] files) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			ZipOutputStream compressed = new ZipOutputStream(out, StandardCharsets.UTF_8);
			for (int i = 0; i < files.length; i++) {
				ZipEntry entry = new ZipEntry(filenames[i]);
				compressed.putNextEntry(entry);
				compressed.write(files[i]);
				compressed.closeEntry();
			}
			compressed.finish();
			compressed.flush();
			compressed.close();
			return out.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static Path getPath(String fileName) {
		Path path;
		if (fileName.contains(":")) {
			path = Paths.get(fileName);
		} else {
			FileSystemView fsv = FileSystemView.getFileSystemView();
			File com = fsv.getHomeDirectory();
			String tempDir = "FileOperationUtil" + File.separator
					+ GeneralUtil.getDate() + File.separator;
			path = Paths.get(com.getPath() + File.separator + tempDir + fileName);
		}
		return path;
	}
	
	/**
	 * @param data 要写入的数据
	 * @param fileName 可支持文件名和带全路径的文件名
	 * @param needNewFile 是否需要创建新文件（将删除fileName指向的原有文件）
	 */
	public static void writeToFile(byte[] data, String fileName, boolean needNewFile) {
		Path path = getPath(fileName);
		try {
			Path dir = path.getParent();
			if (Files.notExists(dir)) {
				Files.createDirectories(dir);
				Files.createFile(path);
			} else {
				if (needNewFile) {
					Files.deleteIfExists(path);
					Files.createFile(path);
				} else {
					if (Files.notExists(path)) {
						Files.createFile(path);
					} else {
						Map<String,Object> attrs = Files.readAttributes(path, "size");
						System.out.println(fileName + "已存在，开始追加数据，原文件大小：\n" + attrs.get("size"));
					}
				}
			}
			Files.write(path, data, StandardOpenOption.APPEND);
			System.out.println(fileName + "写入完毕，本次写入的数据大小为：" + data.length + "。");
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 方法已废弃，可使用Files.createDirectories取代。
	 * 存档备用，比如每创建一级就要额外做一些操作的特殊场景。
	 */
	@SuppressWarnings("unused")
	@Deprecated
	private static void mkdirForFile(String fullFileName) {
		char sep = File.separatorChar;
		int index = fullFileName.indexOf(sep);
		int end = fullFileName.lastIndexOf(sep);
		if (index < 0 || index == end) {//路径中没有或者只有一个分隔符的情况
			System.out.println("无法给此文件创建文件夹：\n" + fullFileName);
			return;
		}
		while (index < end) {
			int nextIndex = fullFileName.indexOf(sep, index+1);
			if (nextIndex == -1) {
				break;
			}
			String dir = fullFileName.substring(0, nextIndex);
			Path path = Paths.get(dir);
			if (Files.notExists(path)) {
				try {
					Files.createDirectory(path);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			index = nextIndex;
		}
	}
	
	public static void renameToBackup(Path path) {
		if (Files.exists(path)) {
			Path backupDir = FileOperationUtil.getPath("." + path.getFileName() + ".bak."
					+ System.currentTimeMillis());
			System.out.println("重命名备份：【" + path + "】重命名为【" + backupDir + "】");
			try {
				Files.move(path, backupDir);
			} catch (IOException e) {//备选：dir.toFile().renameTo(backupDir.toFile())
				throw new RuntimeException(e);
			}
		}
	}
	
}